//
//  JRMASConstraint.m
//  JRMASonry
//
//  Created by Nick Tymchenko on 1/20/14.
//

#import "JRMASConstraint.h"
#import "JRMASConstraint+Private.h"

#define JRMASMethodNotImplemented() \
    @throw [NSException exceptionWithName:NSInternalInconsistencyException \
                                   reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \
                                 userInfo:nil]

@implementation JRMASConstraint

#pragma mark - Init

- (id)init {
	NSAssert(![self isMemberOfClass:[JRMASConstraint class]], @"JRMASConstraint is an abstract class, you should not instantiate it directly.");
	return [super init];
}

#pragma mark - NSLayoutRelation proxies

- (JRMASConstraint * (^)(id))equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

- (JRMASConstraint * (^)(id))JRMAS_equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

- (JRMASConstraint * (^)(id))greaterThanOrEqualTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual);
    };
}

- (JRMASConstraint * (^)(id))JRMAS_greaterThanOrEqualTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual);
    };
}

- (JRMASConstraint * (^)(id))lessThanOrEqualTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual);
    };
}

- (JRMASConstraint * (^)(id))JRMAS_lessThanOrEqualTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual);
    };
}

#pragma mark - JRMASLayoutPriority proxies

- (JRMASConstraint * (^)())priorityLow {
    return ^id{
        self.priority(JRMASLayoutPriorityDefaultLow);
        return self;
    };
}

- (JRMASConstraint * (^)())priorityMedium {
    return ^id{
        self.priority(JRMASLayoutPriorityDefaultMedium);
        return self;
    };
}

- (JRMASConstraint * (^)())priorityHigh {
    return ^id{
        self.priority(JRMASLayoutPriorityDefaultHigh);
        return self;
    };
}

#pragma mark - NSLayoutConstraint constant proxies

- (JRMASConstraint * (^)(JRMASEdgeInsets))insets {
    return ^id(JRMASEdgeInsets insets){
        self.insets = insets;
        return self;
    };
}

- (JRMASConstraint * (^)(CGSize))sizeOffset {
    return ^id(CGSize offset) {
        self.sizeOffset = offset;
        return self;
    };
}

- (JRMASConstraint * (^)(CGPoint))centerOffset {
    return ^id(CGPoint offset) {
        self.centerOffset = offset;
        return self;
    };
}

- (JRMASConstraint * (^)(CGFloat))offset {
    return ^id(CGFloat offset){
        self.offset = offset;
        return self;
    };
}

- (JRMASConstraint * (^)(NSValue *value))valueOffset {
    return ^id(NSValue *offset) {
        NSAssert([offset isKindOfClass:NSValue.class], @"expected an NSValue offset, got: %@", offset);
        [self setLayoutConstantWithValue:offset];
        return self;
    };
}

- (JRMASConstraint * (^)(id offset))JRMAS_offset {
    // Will never be called due to macro
    return nil;
}

#pragma mark - NSLayoutConstraint constant setter

- (void)setLayoutConstantWithValue:(NSValue *)value {
    if ([value isKindOfClass:NSNumber.class]) {
        self.offset = [(NSNumber *)value doubleValue];
    } else if (strcmp(value.objCType, @encode(CGPoint)) == 0) {
        CGPoint point;
        [value getValue:&point];
        self.centerOffset = point;
    } else if (strcmp(value.objCType, @encode(CGSize)) == 0) {
        CGSize size;
        [value getValue:&size];
        self.sizeOffset = size;
    } else if (strcmp(value.objCType, @encode(JRMASEdgeInsets)) == 0) {
        JRMASEdgeInsets insets;
        [value getValue:&insets];
        self.insets = insets;
    } else {
        NSAssert(NO, @"attempting to set layout constant with unsupported value: %@", value);
    }
}

#pragma mark - Semantic properties

- (JRMASConstraint *)with {
    return self;
}

- (JRMASConstraint *)and {
    return self;
}

#pragma mark - Chaining

- (JRMASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute __unused)layoutAttribute {
    JRMASMethodNotImplemented();
}

- (JRMASConstraint *)left {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}

- (JRMASConstraint *)top {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}

- (JRMASConstraint *)right {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight];
}

- (JRMASConstraint *)bottom {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottom];
}

- (JRMASConstraint *)leading {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeading];
}

- (JRMASConstraint *)trailing {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailing];
}

- (JRMASConstraint *)width {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeWidth];
}

- (JRMASConstraint *)height {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight];
}

- (JRMASConstraint *)centerX {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterX];
}

- (JRMASConstraint *)centerY {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterY];
}

- (JRMASConstraint *)baseline {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBaseline];
}

#if TARGET_OS_IPHONE || TARGET_OS_TV

- (JRMASConstraint *)leftMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeftMargin];
}

- (JRMASConstraint *)rightMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRightMargin];
}

- (JRMASConstraint *)topMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTopMargin];
}

- (JRMASConstraint *)bottomMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottomMargin];
}

- (JRMASConstraint *)leadingMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeadingMargin];
}

- (JRMASConstraint *)trailingMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailingMargin];
}

- (JRMASConstraint *)centerXWithinMargins {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterXWithinMargins];
}

- (JRMASConstraint *)centerYWithinMargins {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterYWithinMargins];
}

#endif

#pragma mark - Abstract

- (JRMASConstraint * (^)(CGFloat multiplier))multipliedBy { JRMASMethodNotImplemented(); }

- (JRMASConstraint * (^)(CGFloat divider))dividedBy { JRMASMethodNotImplemented(); }

- (JRMASConstraint * (^)(JRMASLayoutPriority priority))priority { JRMASMethodNotImplemented(); }

- (JRMASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { JRMASMethodNotImplemented(); }

- (JRMASConstraint * (^)(id key))key { JRMASMethodNotImplemented(); }

- (void)setInsets:(JRMASEdgeInsets __unused)insets { JRMASMethodNotImplemented(); }

- (void)setSizeOffset:(CGSize __unused)sizeOffset { JRMASMethodNotImplemented(); }

- (void)setCenterOffset:(CGPoint __unused)centerOffset { JRMASMethodNotImplemented(); }

- (void)setOffset:(CGFloat __unused)offset { JRMASMethodNotImplemented(); }

#if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV)

- (JRMASConstraint *)animator { JRMASMethodNotImplemented(); }

#endif

- (void)activate { JRMASMethodNotImplemented(); }

- (void)deactivate { JRMASMethodNotImplemented(); }

- (void)install { JRMASMethodNotImplemented(); }

- (void)uninstall { JRMASMethodNotImplemented(); }

@end
